package com.heima.wemedia.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.heima.apis.article.IArticleClient;
import com.heima.common.exception.BusinessException;
import com.heima.miniofile.service.MinIoFileStorageService;
import com.heima.model.common.dtos.ResponseResult;
import com.heima.model.common.enums.AppHttpCodeEnum;
import com.heima.model.wemedia.dtos.WmMaterialDto;
import com.heima.model.wemedia.pojos.WmMaterial;
import com.heima.model.wemedia.pojos.WmNews;
import com.heima.model.wemedia.pojos.WmNewsMaterial;
import com.heima.model.wemedia.pojos.WmUser;
import com.heima.utils.thread.WmThreadLocalUtil;
import com.heima.wemedia.mapper.WmMaterialMapper;
import com.heima.wemedia.mapper.WmNewsMaterialMapper;
import com.heima.wemedia.service.WmMaterialService;
import com.heima.wemedia.utils.common.ServiceUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.stream.Collectors;

@Service
@Transactional
@Slf4j
public class WmMaterialServiceImpl extends ServiceImpl<WmMaterialMapper, WmMaterial> implements WmMaterialService {
    @Autowired
    private MinIoFileStorageService fileStorageService;
    @Autowired
    private WmMaterialMapper wmMaterialMapper;
    @Autowired
    private WmNewsMaterialMapper wmNewsMaterialMapper;
    @Autowired
    private IArticleClient articleClient;

    private static final String DELETED_IMAGE_URL = "http://192.168.200.130:9000/leadnews/deleted.png";
    private static final ObjectMapper objectMapper = new ObjectMapper();
    private static final short COLLECTION = 1;
    private static final short CANCEL_COLLECTION = 0;


    /**
     * 图片/素材上传
     *
     * @param multipartFile 图片/素材信息
     */
    @Override
    public ResponseResult<WmMaterial> uploadPicture(MultipartFile multipartFile) {

        // 1.检查参数
        if (multipartFile == null || multipartFile.getSize() == 0) {
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID);
        }

        // 2.上传图片到minIO中
        String fileName = UUID.randomUUID().toString().replace("-", "");
        String originalFilename = multipartFile.getOriginalFilename();
        String suffix = Objects.requireNonNull(originalFilename)
                .substring(originalFilename.lastIndexOf("."));
        String fileId = null;
        try {
            fileId = fileStorageService.uploadImgFile("", fileName + suffix, multipartFile.getInputStream());
            log.info("上传图片到MinIO中，fileId:{}", fileId);
        } catch (IOException e) {
            log.error("WmMaterialServiceImpl-上传文件失败");
        }

        //3.保存到数据库中
        WmMaterial wmMaterial = new WmMaterial();
        wmMaterial.setUserId(WmThreadLocalUtil.getUser().getId());
        wmMaterial.setUrl(fileId);
        wmMaterial.setIsCollection((short) 0);
        wmMaterial.setType((short) 0);
        wmMaterial.setCreatedTime(new Date());
        save(wmMaterial);

        //4.返回结果
        return ResponseResult.okResult(wmMaterial);
    }

    /**
     * 查询图片素材列表
     *
     * @param dto 查询参数
     */
    @Override
    public ResponseResult<List<WmMaterial>> findList(WmMaterialDto dto) {
        // 1. 检查参数 若参数有误, 默认当前页为1, 每页大小为10
        dto.checkParam();

        // 2.分页查询
        Page<WmMaterial> page = new Page<>(dto.getPage(), dto.getSize());
        LambdaQueryWrapper<WmMaterial> lambdaQueryWrapper = new LambdaQueryWrapper<>();

        // 是否收藏
        if (dto.getIsCollection() != null && dto.getIsCollection() == 1)
            lambdaQueryWrapper.eq(WmMaterial::getIsCollection, dto.getIsCollection());

        // 按照用户查询
        Integer userId = Optional.ofNullable(WmThreadLocalUtil.getUser())
                .map(WmUser::getId)
                .orElseThrow(() -> new BusinessException("用户未登录"));

        lambdaQueryWrapper.eq(WmMaterial::getUserId, userId);

        // 按照时间倒序
        lambdaQueryWrapper.orderByDesc(WmMaterial::getCreatedTime);

        page = page(page, lambdaQueryWrapper);

        // 3.结果返回
        return ServiceUtils.buildPageResponse(
                dto.getPage(),
                dto.getSize(),
                (int) page.getTotal(),
                page.getRecords());
    }

    /**
     * 删除图片/素材
     *
     * @param imgId 图片/素材id
     */
    @Override
    public ResponseResult<String> delPicture(Integer imgId) throws JsonProcessingException {
        // 0. 获取图片的url
        String imgUrl = wmMaterialMapper.selectById(imgId).getUrl();

        // 1. 删除 leadnews-wemedia 数据库的相关图片信息
        dbWeMediaImageDeletion(imgId);

        // 2 远程调用 leadnews-article 服务, 删除 leadnews-article 数据库的相关图片信息
        ResponseResult<String> stringResponseResult = articleClient.markImageAsDeleted(imgUrl);

        // 3. 删除 minio 图片数据
        fileStorageService.delete(imgUrl);

        if (stringResponseResult == null) {
            return ResponseResult.okResult("删除成功");
        }

        return stringResponseResult;
    }

    /**
     * 收藏
     * @param imgID 图片/素材id
     */
    @Override
    public ResponseResult<String> collect(Integer imgID) {
        int isSuccess = toggleFavorite(imgID, COLLECTION);
        return getStringResponseResult(isSuccess);
    }

    /**
     * 取消收藏
     *
     * @param imgID 图片/素材id
     */
    @Override
    public ResponseResult<String> cancelCollect(Integer imgID) {
        int isSuccess = toggleFavorite(imgID, CANCEL_COLLECTION);
        return getStringResponseResult(isSuccess);
    }

    // 判断收藏结果
    private static ResponseResult<String> getStringResponseResult(int isSuccess) {
        switch (isSuccess) {
            case 0: // 收藏失败
                return ResponseResult.errorResult(AppHttpCodeEnum.SERVER_ERROR);
            case 1: // 收藏成功
                return ResponseResult.okResult(AppHttpCodeEnum.SUCCESS.getErrorMessage());
            default: // 参数错误
                return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID);
        }
    }

    /**
     * 收藏/取消收藏
     * @param imgID 图片id
     * @param isCollection 收藏状态 0-取消收藏 1-收藏
     * @return 0-失败 1-成功 2-图片id无效
     */
    private int toggleFavorite(Integer imgID, Short isCollection) {
        // 0. 参数校验
        if (imgID == null || imgID <= 0) {
            return 2;
        }

        // 1. 更新收藏状态
        boolean isSuccess = lambdaUpdate()
                .set(WmMaterial::getIsCollection, isCollection)
                .eq(WmMaterial::getId, imgID)
                .update();

        return isSuccess ? 1 : 0;
    }

    /**
     * 处理数据库 leadnews-wemedia 相关图片删除的全流程
     * 1. 查询图片关联信息
     * 2. 更新引用该图片的内容
     * 3. 删除关联关系
     * 4. 删除图片记录
     */
    private void dbWeMediaImageDeletion(Integer imgId) {
        // 1. 根据 图片id 查询 wm_news_material 表中信息
        List<WmNewsMaterial> imgAssociatedInfo = wmMaterialMapper.queryImgAssociatedInfo(imgId);

        // 1.1. 非空校验 若图片不存在关联信息，则直接删除图片
        if (CollectionUtils.isEmpty(imgAssociatedInfo)) {
            wmMaterialMapper.deleteById(imgId);
        }

        // 2. 获取内容引用和封面引用的图片id集合
        List<Integer> contentQuotation = filterAndMap(imgAssociatedInfo, 0);
        List<Integer> coverQuotation = filterAndMap(imgAssociatedInfo, 1);

        // 3. 将引用的地方替换为 deleted.png 图片
        if (!contentQuotation.isEmpty()) updateReferencedImages(contentQuotation);
        if (!coverQuotation.isEmpty()) updateCoverImages(coverQuotation);

        // 4. 删除 wm_news_material 表中对 wm_news 和 wm_material 的关联
        List<Integer> collect = imgAssociatedInfo.stream()
                .map(WmNewsMaterial::getId)
                .collect(Collectors.toList());

        System.out.println("collect = " + collect);
        if (!collect.isEmpty()) {
            wmNewsMaterialMapper.deleteBatchIds(collect);
        }

        // 5. 删除 wm_material 表中对应的图片
        System.out.println("准备删除图片：" + imgId);
        wmMaterialMapper.deleteById(imgId);
    }

    // 更新封面图片引用
    private void updateCoverImages(List<Integer> coverQuotation) {
        // 1. 查询图片内容 images 信息
        List<WmNews> ImgCover = wmMaterialMapper.queryImages(coverQuotation);

        // 2. 将封面图片设置为  deleted.png
        List<WmNews> collect = ImgCover.stream()
                .filter(wmNews -> wmNews.getImages() != null && !wmNews.getImages().isEmpty())
                .filter(wmNews -> !wmNews.getImages().equals(DELETED_IMAGE_URL))
                .peek(wmNews -> wmNews.setImages(DELETED_IMAGE_URL))
                .collect(Collectors.toList());


        // 3. 更新封面图片
        wmMaterialMapper.updateCoverImg(collect);
    }

    // 更新图片引用
    private void updateReferencedImages(List<Integer> contentQuotation) {
        // 1. 查询图片内容 content 信息
        List<WmNews> ImgContent = wmMaterialMapper.queryImgContent(contentQuotation);

        // 2. 替换内容引用的图片
        List<WmNews> collect = ImgContent.stream()
                .map(this::processWmNews) // 处理图片内容
                .filter(Objects::nonNull)
                .collect(Collectors.toList());


        // 3. 更新图片内容
        if (collect.isEmpty()) return;

        wmMaterialMapper.updateContentImg(collect);
    }

    // 解析 JSON 字符串
    private static List<Map<String, Object>> parseJSONStr(String str) throws JsonProcessingException {
        return objectMapper.readValue(
                str,
                new TypeReference<List<Map<String, Object>>>() {
                }
        );
    }

    // 单独提取的处理图片内容方法, 如果没有要修改的内容, 返回空对象
    private WmNews processWmNews(WmNews wmNews) {
        if (wmNews.getContent() == null || wmNews.getContent().isEmpty()) {
            log.info("没有要修改的图片内容");
            return null;
        }
        try {
            List<Map<String, Object>> contentList = parseJSONStr(wmNews.getContent());
            long imageCount = contentList.stream()
                    .filter(item -> "image".equals(item.get("type")) && item.get("value") != null)
                    .peek(item -> item.put("value", DELETED_IMAGE_URL))
                    .count();

            if (imageCount == 0) {
                log.info("没有要修改的图片内容");
                return null;
            }

            wmNews.setContent(objectMapper.writeValueAsString(contentList));
            return wmNews;
        } catch (JsonProcessingException e) {
            log.error("解析JSON字符串失败, newsId: {}", wmNews.getId(), e);
            throw new RuntimeException("Failed to process WmNews content", e);
        }
    }

    /**
     * 过滤图片关联信息 根据 type 筛选出是内容引用，还是封面引用
     *
     * @param imgAssociatedInfo 图片关联信息
     * @param type 1=内容引用，2=封面引用
     */
    private List<Integer> filterAndMap(List<WmNewsMaterial> imgAssociatedInfo, int type) {
        return imgAssociatedInfo.stream()
                .filter(s -> s.getType() == type)
                .map(WmNewsMaterial::getNewsId)
                .collect(Collectors.toList());
    }
}
